package com.platform.core.service.impl;

import com.google.common.collect.Maps;
import com.platform.comm.constraints.CImport;
import com.querydsl.core.BooleanBuilder;
import com.platform.comm.common.service.CommQueryImportService;
import com.platform.comm.dto.DtoMapper;
import com.platform.comm.entity.CommBatchInfoEntity;
import com.platform.comm.entity.CommDataInfoEntity;
import com.platform.comm.entity.QCommBatchInfoEntity;
import com.platform.comm.entity.QCommDataInfoEntity;
import com.platform.comm.jwt.JwtTokenUtil;
import com.platform.comm.jwt.TokenInfoUtil;
import com.platform.comm.repository.CommDataInfoRepository;
import com.platform.comm.service.ICommBatchInfoBiz;
import com.platform.comm.util.DateTimeUtil;
import com.platform.comm.web.results.DataJsonResult;
import com.platform.core.service.SysUserService;
import com.platform.dto.DepartQo;
import com.platform.dto.UrpMeasureDto;
import com.platform.dto.UserQo;
import com.platform.vo.FileImportDetailDto;
import com.platform.vo.ImportRecordsDto;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.QSort;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.apache.poi.ss.usermodel.CellType.*;

/**
 * 查询导入结果service
 *
 * @author qy 2020/01/09
 */
@Service
@Log4j2
public class CommQueryImportServiceImpl extends BaseEntityBizImpl<CommDataInfoRepository, CommDataInfoEntity>
        implements CommQueryImportService {
    private static final QCommDataInfoEntity qCommDataInfoEntity = QCommDataInfoEntity.commDataImpEntity;
    private static final QCommBatchInfoEntity qCommBatchInfoEntity = QCommBatchInfoEntity.dirBatchInfoEntity;
    private static final String USER_IMPORT = "USER_IMPORT";
    private static final String DEPART_IMPORT = "DEPART_IMPORT";
    private final CommDataInfoBizImpl commDataInfoBizImpl;
    private final ICommBatchInfoBiz commBatchInfoBiz;
    private final DtoMapper dtoMapper;
    private final SysUserService sysUserService;
    private final TokenInfoUtil tokenInfoUtil;

    public CommQueryImportServiceImpl(CommDataInfoBizImpl commDataInfoBizImpl, ICommBatchInfoBiz commBatchInfoBiz,
                                      DtoMapper dtoMapper, CommDataInfoRepository commDataInfoRepository,
                                      SysUserService sysUserService, TokenInfoUtil tokenInfoUtil) {
        super(commDataInfoRepository);
        this.commDataInfoBizImpl = commDataInfoBizImpl;
        this.dtoMapper = dtoMapper;
        this.commBatchInfoBiz = commBatchInfoBiz;
        this.sysUserService = sysUserService;
        this.tokenInfoUtil = tokenInfoUtil;
    }

    @Override
    public Page<ImportRecordsDto> queryImportRecords(Pageable pageable, MultiValueMap<String, String> parameters) {
        Optional.ofNullable(parameters.getFirst("jobType")).orElseThrow(() -> new RuntimeException("文件类型不能为空"));
        JwtTokenUtil.JwtUser jwtUser = tokenInfoUtil.getUser();
        if (pageable.getSort().isUnsorted()) {
            pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), QSort.by(qCommDataInfoEntity.uploadDate.desc()));
        }
        BooleanBuilder bool = new BooleanBuilder();
        if (!jwtUser.isAdmin()) {
            bool.and(qCommBatchInfoEntity.createBy.eq(jwtUser.getId()));
        }

        Page<CommDataInfoEntity> page = queryByPage(bool, pageable, jpqlQuery -> jpqlQuery.from(qCommDataInfoEntity)
                .leftJoin(qCommBatchInfoEntity)
                .on(qCommBatchInfoEntity.commDataId.eq(qCommDataInfoEntity.id))
                .where(builderConditions(parameters)));
        return page.map(entity -> {
            ImportRecordsDto importRecordsDto = dtoMapper.map(entity, ImportRecordsDto.class);
            Optional<CommBatchInfoEntity> results = commBatchInfoBiz.queryOne(qCommBatchInfoEntity.commDataId.eq(entity.getId()));
            results.ifPresent((i) -> {
                importRecordsDto.setJobStatus(i.getJobStatus());
                importRecordsDto.setCreateDeptName(i.getCreateDeptName());
                this.sysUserService.queryById(i.getCreateBy()).ifPresent(user -> importRecordsDto.setCreateUserName(user.getRealName()));
            });
            return importRecordsDto;
        });
    }

    private BooleanBuilder builderConditions(MultiValueMap<String, String> parameters) {
        String jobType = parameters.getFirst("jobType");
        String beginTime = parameters.getFirst("beginTime");
        String endTime = parameters.getFirst("endTime");
        String status = parameters.getFirst("status");
        BooleanBuilder builder = new BooleanBuilder();
        if (StringUtils.isNotEmpty(jobType)) {
            builder.and(qCommBatchInfoEntity.jobType.eq(jobType));
        }
        if (StringUtils.isNotEmpty(beginTime) && StringUtils.isNotEmpty(endTime)) {
            String plusEndTime = DateTimeUtil.toStdDateStr(DateTimeUtil.toLocalDate(endTime).plusDays(1));
            builder.and(qCommBatchInfoEntity.createDate.between(beginTime, plusEndTime));
        }
        if (StringUtils.isNotEmpty(status)) {
            builder.and(qCommBatchInfoEntity.jobStatus.eq(status));
        }
        return builder;
    }

    @Override
    public DataJsonResult<FileImportDetailDto> queryImportDetails(String comId) {
        Optional<CommBatchInfoEntity> commBatchInfoEntity = commBatchInfoBiz.queryOne(qCommBatchInfoEntity.commDataId.eq(comId));
        Optional<CommDataInfoEntity> commDataInfoEntity = commDataInfoBizImpl.queryOne(qCommDataInfoEntity.id.eq(comId));
        FileImportDetailDto file = new FileImportDetailDto();
        commBatchInfoEntity.ifPresent(bat -> {
            dtoMapper.map(bat, file);
            file.setBatchInfoId(bat.getId());
            file.setCachedCount(file.getTotalCount().subtract(file.getValidationCount()));
        });
        commDataInfoEntity.ifPresent(com -> dtoMapper.map(com, file));
        return DataJsonResult.succeed(file);
    }

    @Override
    public DataJsonResult<?> queryErrorExcelInfo(String comId) {
        Optional<CommBatchInfoEntity> commBatchInfoEntity = commBatchInfoBiz.queryOne(qCommBatchInfoEntity.commDataId.eq(comId));
        if (commBatchInfoEntity.isPresent()) {
            try {
                if (StringUtils.isNotEmpty(commBatchInfoEntity.get().getJobResult())) {
                    File file = new File(commBatchInfoEntity.get().getJobResult());
                    InputStream input = new FileInputStream(file);
                    List<Object[]> errorExcelInfos = importErrorExcel(input);
                    return DataJsonResult.succeed(convertToMap(commBatchInfoEntity.get().getJobType(), errorExcelInfos));
                }
            } catch (IOException e) {
                log.info(e.getMessage(), e);
            }
        }
        return new DataJsonResult<String>("没有错误数据");
    }

    private Map<String, Object> convertToMap(String jobType, List<Object[]> errorExcelInfos) {
        Map<String, Object> columns = Maps.newLinkedHashMap();
        List<Map<String, Object>> rows = Lists.newArrayList();
        if (!Objects.requireNonNull(errorExcelInfos).isEmpty()) {
            Object[] title = errorExcelInfos.get(0);
            List<Object> collectTitle = Stream.of(title).filter(Objects::nonNull).collect(Collectors.toList());

            List<String> titleString = Lists.newArrayList();
            //反射拿到实体类中的key
            List<Field> allFields;
            if (USER_IMPORT.equals(jobType)) {
                allFields = getFieldList(UserQo.class);
            } else if (DEPART_IMPORT.equals(jobType)) {
                allFields = getFieldList(DepartQo.class);
            } else {
                allFields = getFieldList(UrpMeasureDto.class);
            }
            //第一步遍历获得columns信息
            List<Field> fields = allFields.stream().filter(field -> field.getAnnotation(CImport.class) != null).collect(Collectors.toList());
            Map<Object, String> compareMap = Maps.newLinkedHashMap();
            fields.forEach(field -> compareMap.put(field.getAnnotation(CImport.class).name(), field.getName()));
            compareMap.put("错误信息", "checkErrMsg");

            collectTitle.forEach(obj -> {
                if (compareMap.containsKey(obj)) {
                    String key = compareMap.get(obj);
                    columns.put(key, obj.toString());
                    titleString.add(key);
                }
            });

            //第二步遍历获得rows信息
            Map<String, Object> tempMap;
            for (int i = 1; i < errorExcelInfos.size(); i++) {
                Object[] objects = errorExcelInfos.get(i);
                tempMap = Maps.newLinkedHashMap();
                for (int j = 0; j < titleString.size(); j++) {
                    tempMap.put(titleString.get(j), objects[j]);
                }
                rows.add(tempMap);
            }
        }
        //第三步封装成map返回
        Map<String, Object> results = Maps.newHashMap();
        results.put("columns", columns);
        results.put("rows", rows);
        return results;
    }

    private List<Object[]> importErrorExcel(InputStream input) {
        log.info("导入解析开始");
        try {
            List<Object[]> list = new ArrayList<>();
            Workbook workbook = WorkbookFactory.create(input);
            Sheet sheet = workbook.getSheetAt(0);
            //获取sheet的行数
            int rows = sheet.getPhysicalNumberOfRows();
            rows = Math.min(rows, 50);
            for (int i = 0; i < rows; i++) {
                //过滤表头行
                /*if (i == 0) {
                    continue;
                }*/
                //获取当前行的数据
                Row row = sheet.getRow(i);
                Object[] objects = new Object[row.getPhysicalNumberOfCells()];
                int index = 0;
                for (Cell cell : row) {
                    if (cell.getCellType().equals(NUMERIC)) {
                        objects[index] = (int) cell.getNumericCellValue();
                    }
                    if (cell.getCellType().equals(STRING)) {
                        objects[index] = cell.getStringCellValue();
                    }
                    if (cell.getCellType().equals(BOOLEAN)) {
                        objects[index] = cell.getBooleanCellValue();
                    }
                    if (cell.getCellType().equals(ERROR)) {
                        objects[index] = cell.getErrorCellValue();
                    }
                    index++;
                }
                list.add(objects);
            }
            log.info("导入文件解析成功！");
            return list;
        } catch (Exception e) {
            log.info("导入文件解析失败！");
            e.printStackTrace();
        }
        return null;
    }


    public static List<Field> getFieldList(Class<?> objClass) {
        Field[] fields = objClass.getDeclaredFields();
        List<Field> resultList = new ArrayList<>(Arrays.asList(fields));
        Class<?> superClass = objClass.getSuperclass();
        if (superClass != null) {
            resultList.addAll(getFieldList(superClass));
        }

        return resultList;
    }
}
